package org.aceor.mddal.common.sqlparser.result;

import com.google.common.base.Objects;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;

/**
 * Created by lxue on 16/3/23.
 */
public class SQLBuilder implements Appendable {
    private static final Logger logger = LoggerFactory.getLogger(SQLBuilder.class);

    private static final String AND = "AND";
    private static final String OR = "OR";
    private static final String WHERE = "WHERE";

    private final Collection<Object> segments = new LinkedList<>();

    private final Map<String, ReplaceToken> replaceTokenMap = new HashMap<>();

    private ColumnAppendToken columnAppendToken;

    private StringBuilder currentSegment;

    private boolean duringExprSkip = false;

    private ReplaceToken whereAppendToken;

    public SQLBuilder() {
        currentSegment = new StringBuilder();
        segments.add(currentSegment);
    }

    /**
     * 生成SQL语句.
     *
     * @return SQL语句
     */
    public String toSQL() {
        StringBuilder result = new StringBuilder();
        for (Object each : segments) {
            result.append(each.toString());
        }
        return filterUnusedBracketPairs(result.toString());
    }

    /**
     * 去除掉无用的括号对,即正则: ( *) 括号里面包含零个或多个空格
     * 如 "select * from a where b = 2 () and (  ) c = 3 "
     * 清理过后 "select * from a where b = 2  and  c = 3 "
     *
     * @param s
     * @return
     */
    private String filterUnusedBracketPairs(String s) {
        return s.replaceAll("\\( *\\)", "");
    }

    /**
     * 进行跳过处理,第一次跳过需要添加跳过标志
     *
     * @return
     */
    public SQLBuilder appendExprSkipToken() {
        currentSegment.append(" ");
        duringExprSkip = true;
        return this;
    }

    /**
     * 添加替换标记
     *
     * @param replaceTokenKey
     * @return
     */
    public SQLBuilder appendReplaceToken(final String replaceTokenKey) {
        ReplaceToken replaceToken;
        if (replaceTokenMap.containsKey(replaceTokenKey)) {
            replaceToken = replaceTokenMap.get(replaceTokenKey);
        } else {
            replaceToken = new ReplaceToken();
            replaceToken.value = replaceTokenKey;
            replaceTokenMap.put(replaceTokenKey, replaceToken);
        }
        segments.add(replaceToken);
        currentSegment = new StringBuilder();
        segments.add(currentSegment);
        return this;
    }

    /**
     * 一般用于表名替换(逻辑表名和实际表名)
     *
     * @param replaceTokenKey   待替换的标记位
     * @param replaceTokenValue 替换成的新值
     * @return
     */
    public SQLBuilder replaceToken(final String replaceTokenKey, final String replaceTokenValue) {
        if (replaceTokenMap.containsKey(replaceTokenKey)) {
            replaceTokenMap.get(replaceTokenKey).value = replaceTokenValue;
        }
        return this;
    }

    /**
     * 注册列增补对象
     */
    public void registerColumnAppendToken() {
        if (null == columnAppendToken) {
            //1. currentSegment ==> print 'select *'

            //2. columnAppendToken ==> print 'appendToken'
            columnAppendToken = new ColumnAppendToken();
            segments.add(columnAppendToken);

            //3. new currentSegment ==> print 'from'
            //用于打断当前的currentSegment
            currentSegment = new StringBuilder();
            segments.add(currentSegment);

            //4. result ==> print 'select *,appendToken from'
        } else {
            logger.warn("SQLBuilder$registerColumnAppendToken==>重复添加'列新增'事件");
        }
    }

    /**
     * 添加增补的列
     *
     * @param columnName
     * @return
     */
    public SQLBuilder appendColumn(final String columnName) {
        if (null == columnAppendToken) {
            throw new NullPointerException("未注册registerColumnAppendToken");
        }
        columnAppendToken.addColumn(columnName);
        return this;
    }

    public SQLBuilder replaceWhereAppendToken(String newCondition) {
        if (null == whereAppendToken) {
            throw new IllegalArgumentException("没有解析到WHERE关键字 ==> 无法使用了whereAppendToken替换, newCondition :" + newCondition);
        }
        if (duringExprSkip) {
            whereAppendToken.value = newCondition + " ";
        } else {
            whereAppendToken.value = newCondition + " AND ";
        }
        return this;
    }

    @Override
    public Appendable append(final CharSequence sql) throws IOException {
        //在Or出现的两重限定之下,这里不会出现OR,因此只需要跳过append and就行
        if (duringExprSkip
                && Objects.equal(sql.toString().trim(), AND)) {
            //skip append
            duringExprSkip = false;
        } else {
            currentSegment.append(sql);
        }
        //由于已经把分库分表字段的Condition语句全部跳过了,这里在where后面留出一个空地,以便后面补上新造的Condition
        if (Objects.equal(sql.toString().trim(), WHERE)) {
            whereAppendToken = new ReplaceToken();
            currentSegment.append(" ");
            segments.add(whereAppendToken);
            currentSegment = new StringBuilder();
            segments.add(currentSegment);
        }
        return this;
    }

    @Override
    public Appendable append(final CharSequence sql, final int start, final int end) throws IOException {
        throw new UnsupportedOperationException();
    }

    @Override
    public Appendable append(final char c) throws IOException {
        currentSegment.append(c);
        return this;
    }

    @Override
    public String toString() {
        StringBuilder result = new StringBuilder();
        for (Object each : segments) {
            result.append(each.toString());
        }
        return result.toString();
    }

    private static class ReplaceToken {
        private String value;

        @Override
        public String toString() {
//            return null == value ? "" : Joiner.on("").join("TOKEN(", value, ")");
            return null == value ? "" : value;
        }
    }

    private static class ColumnAppendToken {
        private StringBuilder value = new StringBuilder();

        public void addColumn(String columnName) {
            value.append(',').append(columnName);
        }

        @Override
        public String toString() {
            return value.toString();
        }
    }
}
